package com.flying.acls.adapter.spring;

import com.flying.acls.domain.context.DOExpressionContextBuilder;
import com.flying.acls.domain.context.ExpressionContext;
import com.flying.acls.domain.permission.ClassPermissionBuilder;
import com.flying.acls.domain.resource.DOPatternBasedResourceBuilder;
import com.flying.acls.domain.service.AclService;
import com.flying.acls.domain.sid.PatternBasedSidBuilder;
import com.flying.acls.model.Permission;
import com.flying.acls.model.Resource;
import com.flying.acls.model.Sid;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.List;

@Slf4j
@Setter
public class AclPermissionEvaluator implements PermissionEvaluator {
    @Autowired
    private DOExpressionContextBuilder contextBuilder;
    @Autowired
    private DOPatternBasedResourceBuilder resourceBuilder;

    @Autowired
    private PatternBasedSidBuilder sidBuilder;
    @Autowired
    private ClassPermissionBuilder permissionBuilder;

    private final AclService aclService;

    public AclPermissionEvaluator(AclService aclService) {
        this.aclService = aclService;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
        if (targetDomainObject == null) return false;
        String className = targetDomainObject.getClass().getName();
        if (!resourceBuilder.support(className)) return false;

        ExpressionContext context = contextBuilder.build(authentication, targetDomainObject);
        List<Resource> requiredResources = resourceBuilder.build(context, className);

        return checkPermission(authentication, context, requiredResources, permission);
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        if (!resourceBuilder.support(targetType)) return false;

        ExpressionContext context = contextBuilder.build(authentication, targetId, targetType);
        List<Resource> requiredResources = resourceBuilder.build(context, targetType);

        return checkPermission(authentication, context, requiredResources, permission);
    }

    private boolean checkPermission(Authentication authentication, ExpressionContext context, List<Resource> requiredResources, Object permission) {
        List<Sid> sids = sidBuilder.build(context, AuthenticationExtractor.getPrincipalName(authentication), AuthenticationExtractor.getRoles(authentication));
        List<Permission> requiredPermission = permissionBuilder.build(permission);
        return aclService.isGranted(context, requiredResources, sids, requiredPermission);
    }
}